﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Diagnostics;
using System.Windows.Data;

namespace Mesuro.Common {
	[TemplatePart( Name = "PART_UpButton", Type = typeof( Button ) )]
	[TemplatePart( Name = "PART_DownButton", Type = typeof( Button ) )]
	public class SpinnerControl : Control {

		#region DP: Value
		public static readonly DependencyProperty ValueProperty = DependencyProperty.RegisterAttached( "Value", typeof( double ), typeof( SpinnerControl ), new FrameworkPropertyMetadata( 0d, FrameworkPropertyMetadataOptions.None,ValueChangedStatic,CoerceValueStatic ), ValidateValueStatic );
		public double Value {
			get { return (double)GetValue( ValueProperty ); }
			set { SetValue( ValueProperty, value ); }
		}
		public static double GetValue( DependencyObject element ) { return (double)element.GetValue( ValueProperty ); }
		public static void SetValue( DependencyObject element, double value ) { element.SetValue( ValueProperty, value ); }
		private static void ValueChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) {
			BindingExpression exp = ((FrameworkElement)sender).GetBindingExpression( ValueProperty );
		}
		private static object CoerceValueStatic( DependencyObject sender, object baseValue) {
			return baseValue;
		}
		private static bool ValidateValueStatic(object value) {
			return true;
		}
		#endregion

		#region DP: Model
		public static readonly DependencyProperty ModelProperty = DependencyProperty.RegisterAttached( "Model", typeof( ISpinnerModel ), typeof( SpinnerControl ), new FrameworkPropertyMetadata( new NumericSpinnerModel(), FrameworkPropertyMetadataOptions.None, ModelChangedStatic ) );
		public ISpinnerModel Model {
			get { return (ISpinnerModel)GetValue( ModelProperty ); }
			set { SetValue( ModelProperty, value ); }
		}
		public static ISpinnerModel GetModel( DependencyObject element ) { return (ISpinnerModel)element.GetValue( ModelProperty ); }
		public static void SetModel( DependencyObject element, ISpinnerModel value ) { element.SetValue( ModelProperty, value ); }
		private static void ModelChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
		#endregion

		#region DP: ContentTemplate
		public static readonly DependencyProperty ContentTemplateProperty = DependencyProperty.RegisterAttached( "ContentTemplate", typeof( DataTemplate ), typeof( SpinnerControl ), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.None, ContentTemplateChangedStatic ) );
		public DataTemplate ContentTemplate {
			get { return (DataTemplate)GetValue( ContentTemplateProperty ); }
			set { SetValue( ContentTemplateProperty, value ); }
		}
		public static DataTemplate GetContentTemplate( DependencyObject element ) { return (DataTemplate)element.GetValue( ContentTemplateProperty ); }
		public static void SetContentTemplate( DependencyObject element, DataTemplate value ) { element.SetValue( ContentTemplateProperty, value ); }
		private static void ContentTemplateChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
		#endregion

		private Button cmdUp, cmdDown;

		static SpinnerControl() {
			HorizontalContentAlignmentProperty.OverrideMetadata( typeof( SpinnerControl ), new FrameworkPropertyMetadata( HorizontalAlignment.Stretch ) );
			VerticalContentAlignmentProperty.OverrideMetadata( typeof( SpinnerControl ), new FrameworkPropertyMetadata( VerticalAlignment.Stretch ) );
			DefaultStyleKeyProperty.OverrideMetadata( typeof( SpinnerControl ), new FrameworkPropertyMetadata( typeof( SpinnerControl ) ) );
		}
		public SpinnerControl() {
		}

		public override void OnApplyTemplate() {
			base.OnApplyTemplate();
			cmdUp = (Button)GetTemplateChild( "PART_UpButton" );
			cmdDown = (Button)GetTemplateChild( "PART_DownButton" );
			if (cmdUp != null) {
				cmdUp.Click += delegate( object sender, RoutedEventArgs e ) { Value = Model.Next( Value ); };
			}
			if (cmdDown != null) {
				cmdDown.Click += delegate( object sender, RoutedEventArgs e ) {
					Value = Model.Prev( Value );
				};
			}
		}
	}
	public interface ISpinnerModel {
		double Next( double current );
		double Prev( double current );

		bool HasNext( double current );
		bool HasPrev( double current );
	}

	public class NumericSpinnerModel : ISpinnerModel {

		public virtual double Min { get; set; }
		public virtual double Max { get; set; }
		public virtual double Step { get; set; }

		public NumericSpinnerModel() : this( double.MinValue, double.MaxValue, 1 ) { }
		public NumericSpinnerModel( double step ) : this( double.MinValue, double.MaxValue, step ) { }
		public NumericSpinnerModel( double minValue, double maxValue ) : this( minValue, maxValue, 1 ) { }
		public NumericSpinnerModel( double minValue, double maxValue, double step ) {
			Min = minValue;
			Max = maxValue;
			Step = step;
		}

		public virtual double Next( double current ) {
			return Math.Min( Max, current + Step );
		}

		public virtual double Prev( double current ) {
			return Math.Max( Min, current - Step );
		}

		public virtual bool HasNext( double current ) {
			return current != Max;
		}

		public virtual bool HasPrev( double current ) {
			return current != Min;
		}
	}
}
